/// $Id$
// Author : Kyoungho An, Anushi Shah

#include <string>
using namespace std;

#include "ace/Get_Opt.h"  // for parsing command line
#include "ace/Log_Msg.h"  // for logging messages
#include "ace/Sched_Params.h"  // for scheduling params
#include "ace/Task.h"  // for the main thread of control

#include "tao/ORB_Core.h" // the ORB itself
#include "tao/RTCORBA/RTCORBA.h"  // all the RTCORBA capabilities
#include "tao/RTPortableServer/RTPortableServer.h" // for RT POA
#include "tao/RTCORBA/Priority_Mapping_Manager.h"


#include "SegmentNEANode_i.h"  // our implementation header
#include "ftrtcs/SegmentActuatorC.h"
#include "Utils.h"

/**************************************************************************/
// files where the IOR will be output
const ACE_TCHAR *dir = ACE_TEXT ("..");
const ACE_TCHAR *ds_file = ACE_TEXT("dsensor");
const ACE_TCHAR *us_file = ACE_TEXT("usensor");
CORBA::Short poa_priority = 5;  // poa level priority
CORBA::Short obj_priority = 10;  // obj level priority

string ds_ior_file;
string us_ior_file;

Block::Segment::SegmentActuator_var actuator_;


/**************************************************************************/
/// parse arguments
int parse_args (int argc, ACE_TCHAR *argv[])
{
  // a utility provided in ACE
  ACE_Get_Opt get_opts (argc, argv, ACE_TEXT("k:o:p:"));
  int c, result;

  while ((c = get_opts ()) != -1)
    switch (c)
      {
      case 'k': // directory where ior is generated. Default is one level up
        dir = get_opts.opt_arg ();
        break;

      case 'o':  // indicates priority level for obj to be executed at
        result = ::sscanf (ACE_TEXT_ALWAYS_CHAR (get_opts.opt_arg ()),
                           "%hd",
                           &obj_priority);
        if (result == 0 || result == EOF)
          ACE_ERROR_RETURN ((LM_ERROR,
                             "Unable to process <-o> option"),
                            -1);
        break;

      case 'p':  // indicates priority level for POA
        result = ::sscanf (ACE_TEXT_ALWAYS_CHAR (get_opts.opt_arg ()),
                           "%hd",
                           &poa_priority);
        if (result == 0 || result == EOF)
          ACE_ERROR_RETURN ((LM_ERROR,
                             "Unable to process <-p> option"),
                            -1);
        break;

      case '?':
      default:
        ACE_ERROR_RETURN ((LM_ERROR,
                           "usage:  %s "
                           "-k <directory of IOR file> "
                           "-o <obj-level priority> "
                           "-p <POA-level priority> "
                           "\n",
                           argv [0]),
                          -1);
      }

  // create the full file names for ior files

   ds_ior_file = 
    string (dir) + 
    ACE_DIRECTORY_SEPARATOR_CHAR +
    string (ds_file);

   us_ior_file = 
    string (dir) + 
    ACE_DIRECTORY_SEPARATOR_CHAR +
    string (us_file);


  // Indicates sucessful parsing of the command line
  return 0;
}

//************* Start of NEAInternal Server Task *************//

class NEAInternalServerTask : public ACE_Task_Base
{
public:
  // constructor
  NEAInternalServerTask (ACE_Thread_Manager &thread_manager,
        CORBA::ORB_ptr orb);

  // this is where all the logic for the thread goes in
  int svc (void);

private:
  CORBA::ORB_var orb_;
};


NEAInternalServerTask::NEAInternalServerTask (ACE_Thread_Manager &thread_manager,
            CORBA::ORB_ptr orb)
  : ACE_Task_Base (&thread_manager),
    orb_ (CORBA::ORB::_duplicate (orb))
{
  
}


int
NEAInternalServerTask::svc (void)
{
  try
    {
      ACE_DEBUG ((LM_DEBUG,
                  ACE_TEXT ("NEANodeInternalServer: Inside the SVC method\n")));
      
      ACE_DEBUG ((LM_DEBUG,
                  ACE_TEXT ("NEANodeInternalServer: acquire the RTORB\n")));

      CORBA::Object_var object =
        this->orb_->resolve_initial_references ("RTORB");
      
      if (CORBA::is_nil (object.in ())) {
        ACE_ERROR ((LM_ERROR,
                    ACE_TEXT ("NEANodeInternalServer: Cannot acquire RTORB\n")));
        return 1;
      }
      
      RTCORBA::RTORB_var rt_orb = RTCORBA::RTORB::_narrow (object.in ());
     
      if (CORBA::is_nil (rt_orb.in ())) {
        ACE_ERROR ((LM_ERROR,
                    ACE_TEXT ("NEANodeInternalServer: Cannot narrow to RTORB\n")));
        return 1;
      }
      
      try 
        {
          ACE_DEBUG ((LM_DEBUG,
                      ACE_TEXT ("NEANodeInternalServer: demonstrating an exception\n")));
         
          CORBA::String_var rtior = 
            this->orb_->object_to_string (rt_orb.in ());
        } 
      catch (const CORBA::Exception& e) 
        {
          e._tao_print_exception ("Exception caught");
          ACE_DEBUG ((LM_DEBUG,
                      ACE_TEXT ("NEANodeInternalServer: resuming from this exception\n")));
        }
    
      ACE_DEBUG ((LM_DEBUG,
                  ACE_TEXT ("NEANodeInternalServer: acquire the RootPOA\n")));
      
      object =
        this->orb_->resolve_initial_references ("RootPOA");

      // defensive programming - check for errors
      if (CORBA::is_nil (object.in ())) {
        ACE_ERROR ((LM_ERROR,
                    ACE_TEXT ("NEANodeInternalServer: Cannot acquire RootPOA\n")));
        return 1;
      }
      
      ACE_DEBUG ((LM_DEBUG,
                  ACE_TEXT ("NEANodeInternalServer: narrow it to RT-enabled RootPOA\n")));

      RTPortableServer::POA_var root_poa =
        RTPortableServer::POA::_narrow (object.in ());

      // defensive programming - check for errors
      if (CORBA::is_nil (root_poa.in ())) {
        ACE_ERROR ((LM_ERROR,
                    ACE_TEXT ("NEANodeInternalServer: Cannot narrow to RT-enabled POA\n")));
        return 1;
      }
    
      ACE_DEBUG ((LM_DEBUG,
                  ACE_TEXT ("NEANodeInternalServer: acquire a POA manager\n")));

      PortableServer::POAManager_var poa_manager =
        root_poa->the_POAManager ();

      RTPortableServer::POA_var child_rt_poa =  
        create_poa_with_server_declared_model (rt_orb.in (),
                                               root_poa.in (),
                                               poa_manager.in (),
                                               poa_priority);

      // defensive programming - check for errors
     if (CORBA::is_nil (child_rt_poa.in ())) {
        ACE_ERROR ((LM_ERROR,
                    ACE_TEXT ("NEANodeInternalServer: Cannot acquire child rt poa\n")));
        return 1;
      }

     ACE_DEBUG ((LM_DEBUG,
                  ACE_TEXT ("NEANodeInternalServer: instantiate our implementation\n")));
      Block_Segment_SegmentNEANode_i *servant = 
        new Block_Segment_SegmentNEANode_i (this->orb_.in (),rt_orb.in (), actuator_);

    // defensive programming - check for errors
      if (!servant) {
        ACE_ERROR ((LM_ERROR,
                    ACE_TEXT ("NEANodeInternalServer: Cannot allocate an impl\n")));
        return 1;
      }

      CORBA::Object_var msg_obj;

      ACE_DEBUG ((LM_DEBUG,
                  ACE_TEXT ("NEANodeInternalServer: create down sensor obj reference at child POA priority\n")));
      if (create_and_publish_diag_object (this->orb_.in (),
                                          child_rt_poa.in (),
                                          servant,
                                          -1,
                                          ds_ior_file.c_str (),msg_obj) == -1) {
        ACE_ERROR ((LM_ERROR,
                    ACE_TEXT ("NEANodeInternalServer: Cannot create and register obj with child rt poa\n")));
        return 1;
      }

      ACE_DEBUG ((LM_DEBUG,
                  ACE_TEXT ("NEANodeInternal_Server: create up sensor obj reference with obj level override\n")));
      if (create_and_publish_diag_object (this->orb_.in (),
                                          child_rt_poa.in (),
                                          servant,
                                          obj_priority,
                                          us_ior_file.c_str (),msg_obj) == -1) {
        ACE_ERROR ((LM_ERROR,
                    ACE_TEXT ("NEANodeInternalServer: Cannot create and register obj with child poa at obj level priority\n")));
        return 1;
      }
     
      ACE_DEBUG ((LM_DEBUG,
                  ACE_TEXT ("NEANodeInternal_Server: activate the POA manager\n")));
      poa_manager->activate ();

      ACE_DEBUG ((LM_DEBUG,
                  ACE_TEXT ("NEANodeInternal_Server: Now let the ORB take over\n")));
      this->orb_->run ();
    }
  catch (const CORBA::Exception& ex)
    {
      ex._tao_print_exception ("Exception caught");
      return -1;
    }

  // if and when the ORB returns, we get here
  return 0;

}
//**************End of NEAInternal Server Task **************//

int
ACE_TMAIN(int argc, ACE_TCHAR *argv[])
{

//*************Start of NEANodeInternalClient for Actuator *******************
   try
    {
        ACE_DEBUG ((LM_DEBUG,
                  ACE_TEXT ("NEANodeInternalClient: acquire the ORB\n")));
        CORBA::ORB_var actuator_orb_ =
          CORBA::ORB_init (argc, argv);
      
        // defensive programming - check for errors
        if (CORBA::is_nil (actuator_orb_.in ())) {
          ACE_ERROR ((LM_ERROR,
                      ACE_TEXT ("NEANodeInternalClient: Cannot acquire ORB\n")));
        }
      
	CORBA::String_var ior_ = ACE_TEXT("file://../actuator");

        ACE_DEBUG ((LM_DEBUG,
                  ACE_TEXT ("Grabbing obj ref to Actuator from file: %s\n"),
                  ior_.in ()));

        // let us grab the object reference to the Actuator module
        CORBA::Object_var object =
          actuator_orb_->string_to_object (ior_.in ());

        // check error
        if (CORBA::is_nil (object.in ())) {
        // failure to convert into obj ref
          ACE_ERROR ((LM_ERROR,
                      ACE_TEXT ("Block::Segment::NEANode - bad object ref\n")));
        }

        // now narrow the obj ref to the Actuator type
	actuator_ = 
	  Block::Segment::SegmentActuator::_narrow (object.in ());


        // check error
        if (CORBA::is_nil (actuator_.in ())) {
          // failure to narrow
          ACE_ERROR ((LM_ERROR,
                      ACE_TEXT ("Block::Segment::NEANode - cannot narrow to Diagnostics\n")));
        }

//   =====================================================================
//    Code to make it Client propagated               
//   ====================================================================

          CORBA::Policy_var policy =
            actuator_->_get_policy(RTCORBA::PRIORITY_MODEL_POLICY_TYPE);
        
          RTCORBA::PriorityModelPolicy_var priority_policy = 
             RTCORBA::PriorityModelPolicy::_narrow(policy.in());

          if(CORBA::is_nil(priority_policy.in()))
           {
              ACE_ERROR((LM_ERROR,
                    ACE_TEXT ("Messenger object deos not support Priority Model\n")));
           }

           RTCORBA::PriorityModel priority_model =
           priority_policy->priority_model();

           if(priority_model != RTCORBA::CLIENT_PROPAGATED)
           {
              
              ACE_ERROR((LM_ERROR,
                    ACE_TEXT ("Messenger object deosnot support Client Priority Model\n")));   
           }

           CORBA::Object_var curr_obj =
           actuator_orb_->resolve_initial_references("RTCurrent");
           
           RTCORBA::Current_var current =
           RTCORBA::Current::_narrow(curr_obj.in());
          
          //**************************************
	curr_obj = actuator_orb_->resolve_initial_references ("PriorityMappingManager");
      	RTCORBA::PriorityMappingManager_var mapping_manager =
        RTCORBA::PriorityMappingManager::_narrow (curr_obj.in ());

	RTCORBA::PriorityMapping *pm =
        mapping_manager->mapping ();

        int sched_policy =
        actuator_orb_->orb_core ()->orb_params ()->ace_sched_policy ();

        int max_priority =
        ACE_Sched_Params::priority_max (sched_policy);
        int min_priority =
        ACE_Sched_Params::priority_min (sched_policy);

        CORBA::Short native_priority =
        (max_priority + min_priority) / 2;

        CORBA::Short desired_priority = 0;
	pm->to_CORBA (native_priority, desired_priority);
    
		
// =============================================================================================

#if 0
      object =
        this->orb_->resolve_initial_references ("RTCurrent");

      RTCORBA::Current_var current =
        RTCORBA::Current::_narrow (object.in ());

      // We need to set the client thread CORBA priority
      current->the_priority (get_implicit_thread_CORBA_priority (this->orb_.in ()));
#endif
    }
  catch (const CORBA::Exception& ex)
    {
      ex._tao_print_exception ("Exception caught");
    }

//************* End of NEANodeInternalClient for Actuator*****************



//*************Start of NEANodeInternalServer Thread for Sensors *******************
   try
    {
        ACE_DEBUG ((LM_DEBUG,
                  ACE_TEXT ("NEANodeInternalServer: acquire the ORB\n")));
        CORBA::ORB_var orb =
        CORBA::ORB_init (argc, argv);
      
        // defensive programming - check for errors
        if (CORBA::is_nil (orb.in ())) {
          ACE_ERROR ((LM_ERROR,
                      ACE_TEXT ("NEANodeInternalServer: Cannot acquire ORB\n")));
        return 1;
        }
      
        // parse the arguments
        ACE_DEBUG ((LM_DEBUG,
                    ACE_TEXT ("NEANodeInternalServer: parse arguments\n")));
        int result = parse_args (argc, argv);
        if (result != 0)
          return result;
	
	ACE_Thread_Manager thread_manager;

        NEAInternalServerTask task (thread_manager, orb.in ());

        // Task activation flags. We create a lightweight process and wait to
        // have the thread send its status back.
        long flags =
          THR_NEW_LWP |
          THR_JOINABLE |
          orb->orb_core ()->orb_params ()->thread_creation_flags ();

        ACE_DEBUG ((LM_DEBUG,
                  ACE_TEXT ("NEANode: spawn a task\n")));
        result = task.activate (flags);
        if (result == -1)
        {
          // check what could have gone wrong. Maybe we did not have
          // permissions to run the real-time stuff.
          if (errno == EPERM)
            {
              ACE_ERROR_RETURN ((LM_ERROR,
                                 "Cannot create thread with scheduling policy %s\n"
                                 "because the user does not have the appropriate privileges, terminating program....\n"
                                 "Check svc.conf options and/or run as root\n",
                                 sched_policy_name (orb->orb_core ()->orb_params ()->ace_sched_policy ())),
                                2);
            }
          else
            // Unexpected error.
            ACE_ASSERT (0);
         }
         result = thread_manager.wait();
         ACE_ASSERT (result != -1);
    }
  catch (const CORBA::Exception& ex)
    {
      ex._tao_print_exception ("Exception caught");
      return -1;
    }

//************* End of NEANode_InternalServer Thread for Sensors

   return 0;

}
